/* uboot/sprite/sprite_auto_update.c
 *
 * Copyright (c) 2016 Allwinnertech Co., Ltd.
 * Author: zhouhuacai <zhouhuacai@allwinnertech.com>
 *
 * Update firmware with sdcard storage
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#include <common.h>
#include <malloc.h>
#include <sprite.h>
#include <sunxi_mbr.h>
#include <sunxi_nand.h>
#include <private_toc.h>
#include <private_boot0.h>
#include <sunxi_board.h>
#include <spare_head.h>
#include "sprite_card.h"
#include "sparse/sparse.h"
#include "sprite_verify.h"
#include "firmware/imgdecode.h"
#include <fs.h>
#include "sys_config.h"
#include "sprite_auto_update.h"
#include <usb.h>
#include "./cartoon/sprite_cartoon.h"
#include "fdt_support.h"

DECLARE_GLOBAL_DATA_PTR;

#define IMG_NAME "FIRMWARE.bin"

#define AU_HEAD_BUFF (32 * 1024)
#if defined(CONFIG_SUNXI_SPINOR)
#define AU_ONCE_DATA_DEAL (2 * 1024 * 1024)
#else
#define AU_ONCE_DATA_DEAL (16 * 1024 * 1024)
#endif
#define AU_ONCE_SECTOR_DEAL (AU_ONCE_DATA_DEAL / 512)

static void *imghd;
static void *imgitemhd;
static char *imgname;
static int usb_stor_curr_dev;

extern int do_card0_probe(cmd_tbl_t *cmdtp, int flag, int argc,
			  char *const argv[]);
extern void __dump_mbr(sunxi_mbr_t *mbr_info);
extern void __dump_dlmap(sunxi_download_info *dl_info);
extern int sunxi_sprite_verify_dlmap(void *buffer);
extern int unsparse_probe(char *source, uint length,
			  uint android_format_flash_start);
extern void dump_dram_para(void *dram, uint size);
extern int sunxi_set_secure_mode(void);
extern int sunxi_sprite_download_boot0(void *buffer, int production_media);
extern int sunxi_sprite_download_uboot(void *buffer, int production_media,
				       int generate_checksum);
static int auto_update_firmware_probe(char *name);

int __attribute__((weak)) nand_get_mbr(char *buffer, uint len)
{
	return -1;
}

static int auto_update_firmware_probe(char *name)
{
	imghd = Img_Fat_Open(name);

	if (!imghd) {
		return -1;
	}

	return 0;
}

#ifdef CONFIG_UPDATE_FROM_USB
int sunxi_udisk_check(void)
{
	int ret;
#ifdef CONFIG_USB_STORAGE
	usb_stor_curr_dev = -1; /* current device */
#endif

	usb_stop();
	printf("sunxi_udisk_check...\n");
	ret = usb_init();
	if (ret == 0) {
#ifdef CONFIG_USB_STORAGE
		/* try to recognize storage devices immediately */
		usb_stor_curr_dev = usb_stor_scan(1);
#endif
	}
	return usb_stor_curr_dev;
}

int detect_udisk(void)
{
	int ret;
	ret = sunxi_udisk_check();
	if (ret) {
		printf("No Udisk insert\n");
	} else {
		sprite_led_init();
		printf("Udisk found,update image...\n");
		ret = auto_update_firmware_probe(IMG_NAME);
		if (ret == 0)
			set_boot_work_mode(WORK_MODE_UDISK_UPDATE);
	}
	return 0;
}
#endif

int auto_update_check(void)
{
	int workmode;
#ifdef CONFIG_UPDATE_FROM_USB
	int keyvalue;
	int usb_key, usb_onoff, card_key;
#endif

	workmode = get_boot_work_mode();
	if ((workmode != WORK_MODE_USB_PRODUCT) &&
	    (workmode != WORK_MODE_CARD_PRODUCT)) {
#if defined(CONFIG_UPDATE_FROM_USB)
		keyvalue = uboot_spare_head.boot_ext[0].data[2];
		debug("key %d\n", keyvalue);

		script_parser_fetch("auto_update", "usb_update_key", &usb_key,
				    1);
		if (keyvalue == usb_key) {
			printf("auto usb_update found,update image...\n");
			uboot_spare_head.boot_data.work_mode =
				WORK_MODE_UDISK_UPDATE;
			return 0;
		}

		script_parser_fetch("auto_update", "usb_update_onoff",
				    &usb_onoff, 1);
		if (usb_onoff == 1) {
			return detect_udisk();
		}

		script_parser_fetch("auto_update", "card_update_key", &card_key,
				    1);
		if (keyvalue == card_key) {
			printf("auto card_update found,update image...\n");
			set_boot_work_mode(WORK_MODE_UDISK_UPDATE);

			return 0;
		}
#elif defined(CONFIG_UPDATE_FROM_SDCARD)
		printf("sunxi_card_check...\n");

		if (do_card0_probe(NULL, 0, 0, NULL)) {
			printf("No sd-card insert\n");
		} else {
			printf("sd-card found,update image...\n");
			if (!(auto_update_firmware_probe(IMG_NAME))) {
				set_boot_work_mode(WORK_MODE_CARD_UPDATE);
				printf("found image in sd card.\n");
			} else {
				printf("Not found image in sd card.\n");
			}
		}
	}
#endif

		return 0;
	}

static int au_check_img_valid(void)
{
	/*TBD: we should check the img to make sure updating the correct firmware*/
	return 0;
}

loff_t fat_fs_read(const char *filename, void *buf, int offset, int len)
{
	unsigned long time;
	loff_t len_read;
	ulong load_addr;

	if ((buf == NULL) || (filename == NULL))
		return -1;

	load_addr = (ulong)buf;

#if defined(CONFIG_UPDATE_FROM_USB)
		if (get_boot_work_mode() == WORK_MODE_UDISK_UPDATE ||
		    usb_stor_curr_dev == 0) {
			if (fs_set_blk_dev("usb", "0:1", FS_TYPE_FAT)) {
				printf("fs set usb blk dev fail!\n");
				return -1;
			}
		} else {
			if (fs_set_blk_dev("mmc", "0:0", FS_TYPE_FAT)) {
				printf("fs set mmc blk dev fail!\n");
				return -1;
			}
		}
#elif defined(CONFIG_UPDATE_FROM_SDCARD)
	if (fs_set_blk_dev("mmc", "0", FS_TYPE_FAT)) {
		printf("try mmc 0 fail, now try mmc 0:0!\n");
		if (fs_set_blk_dev("mmc", "0:0", FS_TYPE_FAT)) {
			printf("try mmc 0:0 fail \nfs set mmc blk dev fail!\n");
			return -1;
		}
	}
#endif

		time = get_timer(0);
		fs_read(filename, load_addr, offset, len, &len_read);
		time = get_timer(time);
		if (len_read <= 0)
			return -1;

		printf("%lld bytes read in %lu ms", len_read, time);
#if 0
	if (time > 0) {
		puts(" (");
		print_size(len_read / time * 1000, "/s");
		puts(")");
	}
	puts("\n");
#endif
		return len_read;
	}

static int auto_update_fetch_download_map(sunxi_download_info *dl_map)
{
		imgitemhd = Img_OpenItem(imghd, "12345678", "1234567890DLINFO");
		if (!imgitemhd) {
			return -1;
		}

		if (!Img_Fat_ReadItem(imghd, imgitemhd, imgname, (void *)dl_map,
				      sizeof(sunxi_download_info))) {
			printf("sunxi sprite error : read dl map failed\n");

			return -1;
		}

		Img_CloseItem(imghd, imgitemhd);
		imgitemhd = NULL;

		return sunxi_sprite_verify_dlmap(dl_map);
}

static int auto_update_fetch_mbr(void *img_mbr)
{
		int mbr_num = SUNXI_MBR_COPY_NUM;

		if (get_boot_storage_type() == STORAGE_NOR) {
			mbr_num = 1;
		}

		imgitemhd = Img_OpenItem(imghd, "12345678", "1234567890___MBR");
		if (!imgitemhd) {
			return -1;
		}

		if (!Img_Fat_ReadItem(imghd, imgitemhd, imgname, img_mbr,
				      sizeof(sunxi_mbr_t) * mbr_num)) {
			printf("sunxi sprite error : read mbr failed\n");

			return -1;
		}

		Img_CloseItem(imghd, imgitemhd);
		imgitemhd = NULL;

		return sunxi_sprite_verify_mbr(img_mbr);
}

static int __download_normal_part(dl_one_part_info *part_info,
				  uchar *source_buff)
{
	int ret = -1;
	uint partstart_by_sector;
	uint tmp_partstart_by_sector;
	s64 partsize_by_byte;
	s64 partdata_by_byte;
	s64 tmp_partdata_by_bytes;
	uint onetime_read_sectors;
	uint first_write_bytes;
	uint imgfile_start;
	uint tmp_imgfile_start;
	int partdata_format;
	uint active_verify;
	uint origin_verify;
	uchar verify_data[1024];
	uint *tmp;
	u8 *down_buffer = source_buff + AU_HEAD_BUFF;

	tmp_partstart_by_sector = partstart_by_sector =
		part_info->addrlo;
	partsize_by_byte = part_info->lenlo;
	partsize_by_byte <<= 9;

	imgitemhd = Img_OpenItem(imghd, "RFSFAT16",
				 (char *)part_info->dl_filename);
	if (!imgitemhd) {
		printf("sunxi sprite error: open part %s failed\n",
		       part_info->dl_filename);

		return -1;
	}

	partdata_by_byte = Img_GetItemSize(imghd, imgitemhd);
	if (partdata_by_byte <= 0) {
		printf("sunxi sprite error: fetch part len %s failed\n",
		       part_info->dl_filename);

		goto __download_normal_part_err1;
	}
	printf("partdata hi 0x%x\n", (uint)(partdata_by_byte >> 32));
	printf("partdata lo 0x%x\n", (uint)partdata_by_byte);

	if (partdata_by_byte > partsize_by_byte) {
		printf("sunxi sprite: data size 0x%x is larger than part %s size 0x%x\n",
		       (uint)(partdata_by_byte / 512),
		       part_info->dl_filename,
		       (uint)(partsize_by_byte / 512));

		goto __download_normal_part_err1;
	}

	tmp_partdata_by_bytes = partdata_by_byte;
	if (tmp_partdata_by_bytes >= AU_ONCE_DATA_DEAL) {
		onetime_read_sectors = AU_ONCE_SECTOR_DEAL;
		first_write_bytes    = AU_ONCE_DATA_DEAL;
	} else {
		onetime_read_sectors =
			(tmp_partdata_by_bytes + 511) >> 9;
		first_write_bytes = (uint)tmp_partdata_by_bytes;
	}

	imgfile_start = Img_GetItemOffset(imghd, imgitemhd);
	if (!imgfile_start) {
		printf("sunxi sprite err : cant get part data imgfile_start %s\n",
		       part_info->dl_filename);

		goto __download_normal_part_err1;
	}
	tmp_imgfile_start = imgfile_start;

	if (fat_fs_read(imgname, down_buffer, imgfile_start,
			onetime_read_sectors * 512) !=
	    onetime_read_sectors * 512) {
		printf("sunxi sprite error : read sdcard start %d, total %d failed\n",
		       tmp_imgfile_start, onetime_read_sectors);

		goto __download_normal_part_err1;
	}
	/* position of next data to be read*/
	tmp_imgfile_start += onetime_read_sectors * 512;

	/* check sparse format or not */
	partdata_format =
		unsparse_probe((char *)down_buffer, first_write_bytes,
			       partstart_by_sector);
	if (partdata_format != ANDROID_FORMAT_DETECT) {
		if (sunxi_sprite_write(tmp_partstart_by_sector,
				       onetime_read_sectors,
				       down_buffer) !=
		    onetime_read_sectors) {
			printf("sunxi sprite error: download rawdata error %s\n",
			       part_info->dl_filename);

			goto __download_normal_part_err1;
		}
		tmp_partdata_by_bytes -= first_write_bytes;
		tmp_partstart_by_sector += onetime_read_sectors;

		while (tmp_partdata_by_bytes >= AU_ONCE_DATA_DEAL) {
			/* continue read partition data from img*/
			if (fat_fs_read(imgname, down_buffer,
					tmp_imgfile_start,
					AU_ONCE_DATA_DEAL) !=
			    AU_ONCE_DATA_DEAL) {
				printf("sunxi sprite error : read sdcard start %d, total %d failed\n",
				       tmp_imgfile_start,
				       AU_ONCE_DATA_DEAL);

				goto __download_normal_part_err1;
			}
			if (sunxi_sprite_write(tmp_partstart_by_sector,
					       AU_ONCE_SECTOR_DEAL,
					       down_buffer) !=
			    AU_ONCE_SECTOR_DEAL) {
				printf("sunxi sprite error: download rawdata error %s, start 0x%x, sectors 0x%x\n",
				       part_info->dl_filename,
				       tmp_partstart_by_sector,
				       AU_ONCE_SECTOR_DEAL);

				goto __download_normal_part_err1;
			}
			tmp_imgfile_start += AU_ONCE_SECTOR_DEAL * 512;
			tmp_partdata_by_bytes -= AU_ONCE_DATA_DEAL;
			tmp_partstart_by_sector += AU_ONCE_SECTOR_DEAL;
		}
		if (tmp_partdata_by_bytes > 0) {
			uint rest_sectors =
				(tmp_partdata_by_bytes + 511) >> 9;
			if (fat_fs_read(imgname, down_buffer,
					tmp_imgfile_start,
					rest_sectors * 512) !=
			    rest_sectors * 512) {
				printf("sunxi sprite error : read sdcard start %d, total %d failed\n",
				       tmp_imgfile_start,
				       rest_sectors * 512);

				goto __download_normal_part_err1;
			}
			if (sunxi_sprite_write(tmp_partstart_by_sector,
					       rest_sectors,
					       down_buffer) !=
			    rest_sectors) {
				printf("sunxi sprite error: download rawdata error %s, start 0x%x, sectors 0x%x\n",
				       part_info->dl_filename,
				       tmp_partstart_by_sector,
				       rest_sectors);

				goto __download_normal_part_err1;
			}
		}
	} else {
		if (unsparse_direct_write(down_buffer,
					  first_write_bytes)) {
			printf("sunxi sprite error: download sparse error %s\n",
			       part_info->dl_filename);

			goto __download_normal_part_err1;
		}
		tmp_partdata_by_bytes -= first_write_bytes;

		while (tmp_partdata_by_bytes >= AU_ONCE_DATA_DEAL) {
			if (fat_fs_read(imgname, down_buffer,
					tmp_imgfile_start,
					AU_ONCE_DATA_DEAL) !=
			    AU_ONCE_DATA_DEAL) {
				printf("sunxi sprite error : read sdcard start 0x%x, total 0x%x failed\n",
				       tmp_imgfile_start,
				       AU_ONCE_DATA_DEAL);

				goto __download_normal_part_err1;
			}
			if (unsparse_direct_write(down_buffer,
						  AU_ONCE_DATA_DEAL)) {
				printf("sunxi sprite error: download sparse error %s\n",
				       part_info->dl_filename);

				goto __download_normal_part_err1;
			}
			tmp_imgfile_start += AU_ONCE_SECTOR_DEAL * 512;
			tmp_partdata_by_bytes -= AU_ONCE_DATA_DEAL;
		}
		if (tmp_partdata_by_bytes > 0) {
			uint rest_sectors =
				(tmp_partdata_by_bytes + 511) >> 9;
			if (fat_fs_read(imgname, down_buffer,
					tmp_imgfile_start,
					rest_sectors * 512) !=
			    rest_sectors * 512) {
				printf("sunxi sprite error : read sdcard start 0x%x, total 0x%x failed\n",
				       tmp_imgfile_start,
				       rest_sectors * 512);

				goto __download_normal_part_err1;
			}
			if (unsparse_direct_write(
				    down_buffer,
				    tmp_partdata_by_bytes)) {
				printf("sunxi sprite error: download sparse error %s\n",
				       part_info->dl_filename);

				goto __download_normal_part_err1;
			}
		}
	}

	tick_printf("successed in writting part %s\n", part_info->name);
	ret = 0;
	if (imgitemhd) {
		Img_CloseItem(imghd, imgitemhd);
		imgitemhd = NULL;
	}
	/* verify */
	if (part_info->verify) {
		ret = -1;
		if (part_info->vf_filename[0]) {
			imgitemhd = Img_OpenItem(
				imghd, "RFSFAT16",
				(char *)part_info->vf_filename);
			if (!imgitemhd) {
				printf("sprite update warning: open part %s failed\n",
				       part_info->vf_filename);

				goto __download_normal_part_err1;
			}
			if (!Img_Fat_ReadItem(imghd, imgitemhd, imgname,
					      (void *)verify_data,
					      1024)) {
				printf("sprite update warning: fail to read data from %s\n",
				       part_info->vf_filename);

				goto __download_normal_part_err1;
			}
			if (partdata_format == ANDROID_FORMAT_DETECT) {
				active_verify =
					sunxi_sprite_part_sparsedata_verify();
			} else {
				active_verify =
					sunxi_sprite_part_rawdata_verify(
						partstart_by_sector,
						partdata_by_byte);
			}
			tmp	   = (uint *)verify_data;
			origin_verify = *tmp;
			printf("origin_verify value = %x, active_verify value = %x\n",
			       origin_verify, active_verify);
			if (origin_verify != active_verify) {
				printf("origin checksum=%x, active checksum=%x\n",
				       origin_verify, active_verify);
				printf("sunxi sprite: part %s verify error\n",
				       part_info->dl_filename);

				goto __download_normal_part_err1;
			}
			ret = 0;
		} else {
			printf("sunxi sprite err: part %s unablt to find verify file\n",
			       part_info->dl_filename);
		}
		tick_printf("successed in verify part %s\n",
			    part_info->name);
	} else {
		printf("sunxi sprite err: part %s not need to verify\n",
		       part_info->dl_filename);
	}

__download_normal_part_err1:
	if (imgitemhd) {
		Img_CloseItem(imghd, imgitemhd);
		imgitemhd = NULL;
	}

	return ret;
}

static int __download_udisk(dl_one_part_info *part_info,
			    uchar *source_buff)
{
	HIMAGEITEM imgitemhd = NULL;
	u32 flash_sector;
	s64 packet_len;
	s32 ret = -1, ret1;

	imgitemhd = Img_OpenItem(imghd, "RFSFAT16",
				 (char *)part_info->dl_filename);
	if (!imgitemhd) {
		printf("sunxi sprite error: open part %s failed\n",
		       part_info->dl_filename);

		return -1;
	}

	packet_len = Img_GetItemSize(imghd, imgitemhd);
	if (packet_len <= 0) {
		printf("sunxi sprite error: fetch part len %s failed\n",
		       part_info->dl_filename);

		goto __download_udisk_err1;
	}
	if (packet_len <= CONFIG_FW_BURN_UDISK_MIN_SIZE) {
		printf("download UDISK: the data length of udisk is too small, ignore it\n");

		ret = 1;
		goto __download_udisk_err1;
	}

	flash_sector = sunxi_sprite_size();
	if (!flash_sector) {
		printf("sunxi sprite error: download_udisk, the flash size is invalid(0)\n");

		goto __download_udisk_err1;
	}
	printf("the flash size is %d MB\n", flash_sector / 2 / 1024);
	part_info->lenlo = flash_sector - part_info->addrlo;
	part_info->lenhi = 0;
	printf("UDISK low is 0x%x Sectors\n", part_info->lenlo);
	printf("UDISK high is 0x%x Sectors\n", part_info->lenhi);

	ret = __download_normal_part(part_info, source_buff);
__download_udisk_err1:
	ret1 = Img_CloseItem(imghd, imgitemhd);
	if (ret1 != 0) {
		printf("sunxi sprite error: __download_udisk, close udisk image failed\n");

		return -1;
	}

	return ret;
}

static int __download_sysrecover_part(dl_one_part_info *part_info,
				      uchar *source_buff)
{
	uint partstart_by_sector;
	uint tmp_partstart_by_sector;
	s64 partsize_by_byte;
	s64 partdata_by_byte;
	s64 tmp_partdata_by_bytes;
	uint onetime_read_sectors;
	uint tmp_imgfile_start = 0;
	u8 *down_buffer	= source_buff + AU_HEAD_BUFF;
	int ret		       = -1;

	tmp_partstart_by_sector = partstart_by_sector =
		part_info->addrlo;
	partsize_by_byte = part_info->lenlo;
	partsize_by_byte <<= 9;

	partdata_by_byte = Img_GetSize(imghd);
	if (partdata_by_byte <= 0) {
		printf("sunxi sprite error: fetch part len %s failed\n",
		       part_info->dl_filename);

		goto __download_sysrecover_part_err1;
	}

	if (partdata_by_byte > partsize_by_byte) {
		printf("sunxi sprite: data size 0x%x is larger than part %s size 0x%x\n",
		       (uint)(partdata_by_byte / 512),
		       part_info->dl_filename,
		       (uint)(partsize_by_byte / 512));

		goto __download_sysrecover_part_err1;
	}

	tmp_partdata_by_bytes = partdata_by_byte;
	if (tmp_partdata_by_bytes >= AU_ONCE_DATA_DEAL) {
		onetime_read_sectors = AU_ONCE_SECTOR_DEAL;
	} else {
		onetime_read_sectors =
			(tmp_partdata_by_bytes + 511) >> 9;
	}

	while (tmp_partdata_by_bytes >= AU_ONCE_DATA_DEAL) {
		if (fat_fs_read(imgname, down_buffer, 0,
				onetime_read_sectors * 512) !=
		    onetime_read_sectors * 512) {
			printf("sunxi sprite error : read sdcard start %d, total %d failed\n",
			       tmp_imgfile_start,
			       onetime_read_sectors * 512);

			goto __download_sysrecover_part_err1;
		}
		if (sunxi_sprite_write(tmp_partstart_by_sector,
				       onetime_read_sectors,
				       down_buffer) !=
		    onetime_read_sectors) {
			printf("sunxi sprite error: download rawdata error %s, start 0x%x, sectors 0x%x\n",
			       part_info->dl_filename,
			       tmp_partstart_by_sector,
			       onetime_read_sectors);

			goto __download_sysrecover_part_err1;
		}
		tmp_imgfile_start += onetime_read_sectors * 512;
		tmp_partdata_by_bytes -= onetime_read_sectors * 512;
		tmp_partstart_by_sector += onetime_read_sectors;
	}
	if (tmp_partdata_by_bytes > 0) {
		uint rest_sectors = (tmp_partdata_by_bytes + 511) / 512;
		if (fat_fs_read(imgname, down_buffer, tmp_imgfile_start,
				rest_sectors * 512) !=
		    rest_sectors * 512) {
			printf("sunxi sprite error : read sdcard start %d, total %d failed\n",
			       tmp_imgfile_start, rest_sectors * 512);

			goto __download_sysrecover_part_err1;
		}
		if (sunxi_sprite_write(tmp_partstart_by_sector,
				       rest_sectors,
				       down_buffer) != rest_sectors) {
			printf("sunxi sprite error: download rawdata error %s, start 0x%x, sectors 0x%x\n",
			       part_info->dl_filename,
			       tmp_partstart_by_sector, rest_sectors);

			goto __download_sysrecover_part_err1;
		}
	}
	ret = 0;

__download_sysrecover_part_err1:
	if (imgitemhd) {
		Img_CloseItem(imghd, imgitemhd);
		imgitemhd = NULL;
	}

	return ret;
}

static int auto_update_deal_part(sunxi_download_info *dl_map)
{
	dl_one_part_info *part_info;
	int ret = -1;
	int ret1;
	int i		 = 0;
	uchar *down_buff = NULL;
	int rate;

	if (!dl_map->download_count) {
		printf("sunxi sprite: no part need to write\n");
		return 0;
	}

	rate = (70 - 10) / dl_map->download_count;

	if (sunxi_sprite_init(1)) {
		printf("sunxi sprite err: init flash err\n");
		return -1;
	}

	/*
* for fatload, if not aligned,a misaligned buffer warning will be printed
* and performance will suffer for the load.
*/
	down_buff = (uchar *)malloc_aligned(
		AU_ONCE_DATA_DEAL + AU_HEAD_BUFF, ARCH_DMA_MINALIGN);
	if (!down_buff) {
		printf("sunxi sprite err: unable to malloc memory for sunxi_sprite_deal_part\n");
		goto __auto_update_deal_part_err1;
	}

	for (part_info = dl_map->one_part_info, i = 0;
	     i < dl_map->download_count; i++, part_info++) {
		tick_printf("begin to download part %s\n",
			    part_info->name);
		if (!strncmp("UDISK", (char *)part_info->name,
			     strlen("UDISK"))) {
			ret1 = __download_udisk(part_info, down_buff);
			if (ret1 < 0) {
				printf("sunxi sprite err: sunxi_sprite_deal_part, download_udisk failed\n");

				goto __auto_update_deal_part_err2;
			} else if (ret1 > 0) {
				printf("do NOT need download UDISK\n");
			}
		}
		/* sysrecovery partition: burn the whole img*/
		else if (!strncmp("sysrecovery",
				  (char *)part_info->name,
				  strlen("sysrecovery"))) {
			ret1 = __download_sysrecover_part(part_info,
							  down_buff);
			if (ret1 != 0) {
				printf("sunxi sprite err: sunxi_sprite_deal_part, download sysrecovery failed\n");

				goto __auto_update_deal_part_err2;
			}
		}
		/*private partition: check if need to burn private data*/
		else if (!strncmp("private", (char *)part_info->name,
				  strlen("private"))) {
			if (1) {
				/*need to burn  private part*/
				printf("NEED down private part\n");
				ret1 = __download_normal_part(
					part_info, down_buff);
				if (ret1 != 0) {
					printf("sunxi sprite err: sunxi_sprite_deal_part, download private failed\n");

					goto __auto_update_deal_part_err2;
				}
			} else {
				printf("IGNORE private part\n");
			}
		} else {
			ret1 = __download_normal_part(part_info,
						      down_buff);
			if (ret1 != 0) {
				printf("sunxi sprite err: sunxi_sprite_deal_part, download normal failed\n");

				goto __auto_update_deal_part_err2;
			}
		}
		//sprite_cartoon_upgrade(10 + rate * (i + 1));
		tick_printf("successed in download part %s\n",
			    part_info->name);
	}

	ret = 0;

__auto_update_deal_part_err1:
	sunxi_sprite_exit(1);

__auto_update_deal_part_err2:

	if (down_buff) {
		free_aligned(down_buff);
	}

	return ret;
}

static int auto_update_deal_boot0(int production_media)
{
	char buffer[1 * 1024 * 1024];
	uint item_original_size;

	if (gd->bootfile_mode == SUNXI_BOOT_FILE_NORMAL ||
	    gd->bootfile_mode == SUNXI_BOOT_FILE_PKG) {
		if (production_media == STORAGE_NAND) {
			imgitemhd = Img_OpenItem(imghd, "BOOT    ",
						 "BOOT0_0000000000");
		} else if (production_media == STORAGE_NOR) {
			imgitemhd = Img_OpenItem(imghd, "12345678",
						 "1234567890BNOR_0");
		} else {
			imgitemhd = Img_OpenItem(imghd, "12345678",
						 "1234567890BOOT_0");
		}
	} else {
		imgitemhd = Img_OpenItem(imghd, "12345678",
					 "TOC0_00000000000");
	}

	if (!imgitemhd) {
		printf("sprite update error: fail to open boot0 item\n");
		return -1;
	}

	/*get boot0 size*/
	item_original_size = Img_GetItemSize(imghd, imgitemhd);
	if (!item_original_size) {
		printf("sprite update error: fail to get boot0 item size\n");
		return -1;
	}
	/* read boot0 */
	if (!Img_Fat_ReadItem(imghd, imgitemhd, imgname, (void *)buffer,
			      1 * 1024 * 1024)) {
		printf("update error: fail to read data from for boot0\n");
		return -1;
	}
	Img_CloseItem(imghd, imgitemhd);
	imgitemhd = NULL;
	if (sunxi_sprite_download_boot0(buffer, production_media)) {
		printf("update error: fail to write boot0\n");
		return -1;
	}

	return 0;
}

int auto_update_deal_uboot(int production_media)
{
	char buffer[4 * 1024 * 1024];
	uint item_original_size;
	if (gd->bootfile_mode == SUNXI_BOOT_FILE_NORMAL) {
		imgitemhd = Img_OpenItem(imghd, "12345678",
					 "UBOOT_0000000000");
	} else if (gd->bootfile_mode == SUNXI_BOOT_FILE_PKG) {
		if (production_media == STORAGE_NOR)
			imgitemhd = Img_OpenItem(imghd, "12345678",
						 "BOOTPKG-NOR00000");
		else
			imgitemhd = Img_OpenItem(imghd, "12345678",
						 "BOOTPKG-00000000");
	} else {
		imgitemhd = Img_OpenItem(imghd, "12345678",
					 "TOC1_00000000000");
	}

	if (!imgitemhd) {
		printf("sprite update error: fail to open uboot item\n");
		return -1;
	}
	/* get uboot size */
	item_original_size = Img_GetItemSize(imghd, imgitemhd);
	if (!item_original_size) {
		printf("sprite update error: fail to get uboot item size\n");
		return -1;
	}
	/* read uboot */
	if (!Img_Fat_ReadItem(imghd, imgitemhd, imgname, (void *)buffer,
			      4 * 1024 * 1024)) {
		printf("update error: fail to read data from for uboot\n");
		return -1;
	}
	Img_CloseItem(imghd, imgitemhd);
	imgitemhd = NULL;

	if (sunxi_sprite_download_uboot(buffer, production_media, 0)) {
		printf("update error: fail to write uboot\n");
		return -1;
	}
	printf("sunxi_sprite_deal_uboot ok\n");

	return 0;
}

int sunxi_auto_update_main(void)
{
	int production_media;
	uchar img_mbr[1024 * 1024];
	sunxi_download_info dl_map;
	int mbr_num = SUNXI_MBR_COPY_NUM;
	int nodeoffset;
	int processbar_direct = 0;
	int work_mode	 = get_boot_work_mode();
#if defined(CONFIG_USB_STORAGE) || defined(CONFIG_UPDATE_FROM_USB)
	if (work_mode == WORK_MODE_UDISK_UPDATE ||
	    usb_stor_curr_dev == 0) {
		if (usb_stor_scan(1)) {
			if (sunxi_udisk_check()) {
				printf("No Udisk insert\n");
				return -1;
			}
		} else
			printf("sunxi usb update begin\n");
	} else
#elif defined(CONFIG_UPDATE_FROM_SDCARD)
	if (work_mode == WORK_MODE_CARD_UPDATE) {
		if (do_card0_probe(NULL, 0, 0, NULL)) {
			return -1;
		} else {
			printf("sunxi card update begin\n");
		}
	}
#endif
	tick_printf("sunxi update begin\n");
	nodeoffset = fdt_path_offset(working_fdt, FDT_PATH_CARD_BOOT);
	if (nodeoffset >= 0) {
		if (fdt_getprop_u32(working_fdt, nodeoffset,
				    "processbar_direct",
				    (uint32_t *)&processbar_direct) < 0)
			processbar_direct = 0;
	}

	production_media = get_boot_storage_type();

	imgname = malloc(strlen(IMG_NAME) + 1);
	if (imgname == NULL)
		return -1;

	strcpy(imgname, IMG_NAME);
	//sprite_cartoon_create(processbar_direct);

	if (auto_update_firmware_probe(imgname)) {
		printf("sunxi sprite firmware probe fail\n");

		return -1;
	}

	if (au_check_img_valid() < 0) {
		printf("image file invalid! \n");
		return -1;
	}

	//sprite_cartoon_upgrade(5);

	/* download dlmap file to get the download files*/
	tick_printf("fetch download map\n");
	if (auto_update_fetch_download_map(&dl_map)) {
		printf("sunxi sprite error : fetch download map error\n");

		return -1;
	}
	__dump_dlmap(&dl_map);

	//fetch mbr iterm
	tick_printf("fetch mbr\n");
	if (auto_update_fetch_mbr(&img_mbr)) {
		printf("sunxi sprite error : fetch mbr error\n");

		return -1;
	}
	__dump_mbr((sunxi_mbr_t *)img_mbr);

	/* according to the mbr,erase or protect data*/
	tick_printf("begin to erase flash\n");
	nand_get_mbr((char *)img_mbr, 16 * 1024);
	if (sunxi_sprite_erase_flash(img_mbr)) {
		printf("sunxi sprite error: erase flash err\n");

		return -1;
	}
	tick_printf("successed in erasing flash\n");

	if (production_media == STORAGE_NOR) {
		mbr_num = 1;
	}

	if (sunxi_sprite_download_mbr(img_mbr,
				      sizeof(sunxi_mbr_t) * mbr_num)) {
		printf("sunxi sprite error: download mbr err\n");

		return -1;
	}
	//sprite_cartoon_upgrade(10);

	tick_printf("begin to download part\n");
	/* start burning partition data*/
	if (auto_update_deal_part(&dl_map)) {
		printf("sunxi sprite error : download part error\n");

		return -1;
	}
	tick_printf("successed in downloading part\n");
	//sprite_cartoon_upgrade(80);
	sunxi_sprite_exit(1);

	if (auto_update_deal_uboot(production_media)) {
		printf("sunxi sprite error : download uboot error\n");

		return -1;
	}
	tick_printf("successed in downloading uboot\n");
	//sprite_cartoon_upgrade(90);

	if (auto_update_deal_boot0(production_media)) {
		printf("sunxi sprite error : download boot0 error\n");

		return -1;
	}
	tick_printf("successed in downloading boot0\n");
	//sprite_cartoon_upgrade(100);

	//sprite_uichar_printf("CARD OK\n");
	tick_printf("update firmware success \n");
	mdelay(3000);

	return 0;
}
